#include "bridge_for_render.h"
#include "DetourAlloc.h"
#include <new>
#include "DetourLog.h"
#include "DetourCommon.h"
#include "draw_helper.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "DetourTileCache.h"
#include "DetourTileCacheBuilder.h"

RECAST_API void get_mesh_info(Race* ptr, int& vcnt, int& tcnt)
{
	vcnt = tcnt = 0;
	if (ptr)
	{
		ptr->get_mesh_verts(vcnt);
		vcnt *= 3;
		ptr->get_mesh_triangles(tcnt);
		tcnt *= 3;
	}
}

RECAST_API void get_mesh_vertexs(Race* ptr, FPRaw* data, int length)
{
	if (ptr)
	{
		int vcnt = 0;
		const fixed64* src = ptr->get_mesh_verts(vcnt);
		vcnt *= 3;
		int cpcnt = dtMin(length, vcnt);
		for (int i = 0; i < cpcnt;++i)
		{
			data[i] = src[i];
		}
	}
}

RECAST_API void get_mesh_triangles(Race* ptr, int* data, int length)
{
	if (ptr)
	{
		int tcnt = 0;
		const int* src = ptr->get_mesh_triangles(tcnt);
		tcnt *= 3;
		int cpcnt = dtMin(length, tcnt);
		for (int i = 0; i < cpcnt; ++i)
		{
			data[i] = src[i];
		}
	}
}

static bool drawMeshTile(const dtNavMesh& mesh, const dtNavMeshQuery* query,
	const dtMeshTile* tile, unsigned char flags,FPRaw* data,int dlength, int& dlen, unsigned int* colors,int clength,int& clen)
{
	dtPolyRef base = mesh.getPolyRefBase(tile);

	int tileNum = mesh.decodePolyIdTile(base);
	const unsigned int tileColor = duIntToCol(tileNum, 128);

	//dd->begin(DU_DRAW_TRIS);
	for (int i = 0; i < tile->header->polyCount; ++i)
	{
		const dtPoly* p = &tile->polys[i];
		if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)	// Skip off-mesh links.
			continue;

		const dtPolyDetail* pd = &tile->detailMeshes[i];

		unsigned int col;
		if (query && query->isInClosedList(base | (dtPolyRef)i))
			col = duRGBA(255, 196, 0, 64);
		else
		{
			if (flags & DU_DRAWNAVMESH_COLOR_TILES)
				col = tileColor;
			else
				col = duTransCol(areaToCol(p->getArea()), 64);
		}

		for (int j = 0; j < pd->triCount; ++j)
		{
			const unsigned char* t = &tile->detailTris[(pd->triBase + j) * 4];
			for (int k = 0; k < 3; ++k)
			{
				fixed64* vp = 0;
				if (t[k] < p->vertCount)
				{
					vp = &tile->verts[p->verts[t[k]] * 3];
				}
				else
				{
					vp = &tile->detailVerts[(pd->vertBase + t[k] - p->vertCount) * 3];
				}

				//UnityLog("[%d]:%.2lf,%.2lf,%.2lf\n", dlen,(double)vp[0], (double)vp[1], (double)vp[2]);
				if (dlen + 3 > dlength || clen + 1 > clength)
				{
					return false;
				}

				colors[clen++] = col;
				data[dlen] = vp[0];
				data[dlen + 1] = vp[1];
				data[dlen + 2] = vp[2];
				dlen += 3;
			}
		}
	}
	return true;
}

RECAST_API void get_nav_mesh_graphics(Race* ptr, unsigned char flags, FPRaw* data, int& dlen, unsigned int* colors, int& clen)
{
	if (!ptr || !data || !colors)
		return;

	const dtNavMesh* pkmesh = ptr->get_mesh();
	if (!pkmesh)
		return;
	const dtNavMesh& mesh = *pkmesh;
	int mdlen = dlen;
	dlen = 0;
	int mclen = clen;
	clen = 0;

	for (int i = 0; i < mesh.getMaxTiles(); ++i)
	{
		const dtMeshTile* tile = mesh.getTile(i);
		if (!tile->header) continue;
		if (!drawMeshTile(mesh, 0, tile, flags, data, mdlen, dlen, colors, mclen, clen))
			break;
	}
}

static void duAppendCylinder(const fixed64& minx, const fixed64& miny, const fixed64& minz,
	const fixed64& maxx, const fixed64& maxy, const fixed64& maxz, unsigned int col,FPRaw* data,int& dlen,
	unsigned int * colors,int& clen,int vmax,int cmax)
{
	static const int NUM_SEG = 16;
	static fixed64 dir[NUM_SEG * 2];
	static bool init = false;
	if (!init)
	{
		init = true;
		for (int i = 0; i < NUM_SEG; ++i)
		{
			const fixed64 a = i / NUM_SEG * fixed64::DPI;
			dir[i * 2] = fixedCos(a);
			dir[i * 2 + 1] = fixedSin(a);
		}
	}

	unsigned int col2 = duMultCol(col, 160);

	const fixed64 cx = (maxx + minx) / 2;
	const fixed64 cz = (maxz + minz) / 2;
	const fixed64 rx = (maxx - minx) / 2;
	const fixed64 rz = (maxz - minz) / 2;

	for (int i = 2; i < NUM_SEG; ++i)
	{
		const int a = 0, b = i - 1, c = i;
		dd_vertex(cx + dir[a * 2 + 0] * rx, miny, cz + dir[a * 2 + 1] * rz, col2,data,dlen, colors,clen,vmax,cmax);
		dd_vertex(cx + dir[b * 2 + 0] * rx, miny, cz + dir[b * 2 + 1] * rz, col2, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[c * 2 + 0] * rx, miny, cz + dir[c * 2 + 1] * rz, col2, data, dlen, colors, clen, vmax, cmax);
	}
	for (int i = 2; i < NUM_SEG; ++i)
	{
		const int a = 0, b = i, c = i - 1;
		dd_vertex(cx + dir[a * 2 + 0] * rx, maxy, cz + dir[a * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[b * 2 + 0] * rx, maxy, cz + dir[b * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[c * 2 + 0] * rx, maxy, cz + dir[c * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
	}
	for (int i = 0, j = NUM_SEG - 1; i < NUM_SEG; j = i++)
	{
		dd_vertex(cx + dir[i * 2 + 0] * rx, miny, cz + dir[i * 2 + 1] * rz, col2, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[j * 2 + 0] * rx, miny, cz + dir[j * 2 + 1] * rz, col2, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[j * 2 + 0] * rx, maxy, cz + dir[j * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);

		dd_vertex(cx + dir[i * 2 + 0] * rx, miny, cz + dir[i * 2 + 1] * rz, col2, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[j * 2 + 0] * rx, maxy, cz + dir[j * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[i * 2 + 0] * rx, maxy, cz + dir[i * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
	}
}

void duAppendCylinderWire(const fixed64& minx, const fixed64& miny, const fixed64& minz,
	const fixed64& maxx, const fixed64& maxy, const fixed64& maxz, unsigned int col, FPRaw* data, int& dlen,
	unsigned int* colors, int& clen, int vmax, int cmax)
{
	static const int NUM_SEG = 16;
	static fixed64 dir[NUM_SEG * 2];
	static bool init = false;
	if (!init)
	{
		init = true;
		for (int i = 0; i < NUM_SEG; ++i)
		{
			const fixed64 a = fixed64(i) / NUM_SEG * fixed64::DPI;
			dir[i * 2] = dtMathCosf(a);
			dir[i * 2 + 1] = dtMathSinf(a);
		}
	}

	const fixed64 cx = (maxx + minx) / 2;
	const fixed64 cz = (maxz + minz) / 2;
	const fixed64 rx = (maxx - minx) / 2;
	const fixed64 rz = (maxz - minz) / 2;

	for (int i = 0, j = NUM_SEG - 1; i < NUM_SEG; j = i++)
	{
		dd_vertex(cx + dir[j * 2 + 0] * rx, miny, cz + dir[j * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[i * 2 + 0] * rx, miny, cz + dir[i * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[j * 2 + 0] * rx, maxy, cz + dir[j * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[i * 2 + 0] * rx, maxy, cz + dir[i * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
	}
	for (int i = 0; i < NUM_SEG; i += NUM_SEG / 4)
	{
		dd_vertex(cx + dir[i * 2 + 0] * rx, miny, cz + dir[i * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
		dd_vertex(cx + dir[i * 2 + 0] * rx, maxy, cz + dir[i * 2 + 1] * rz, col, data, dlen, colors, clen, vmax, cmax);
	}
}

static void drawObstacles(const dtTileCache* tc,FPRaw* data, int& dlen, unsigned int* colors, int& clen,int vmax,int cmax)
{
	// Draw obstacles
	for (int i = 0; i < tc->getObstacleCount(); ++i)
	{
		const dtTileCacheObstacle* ob = tc->getObstacle(i);
		if (ob->state == DT_OBSTACLE_EMPTY) continue;
		fixed64 bmin[3], bmax[3];
		tc->getObstacleBounds(ob, bmin, bmax);

		unsigned int col = 0;
		if (ob->state == DT_OBSTACLE_PROCESSING)
			col = duRGBA(255, 255, 0, 128);
		else if (ob->state == DT_OBSTACLE_PROCESSED)
			col = duRGBA(255, 192, 0, 192);
		else if (ob->state == DT_OBSTACLE_REMOVING)
			col = duRGBA(220, 0, 0, 128);

		duAppendCylinder(bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], col,data,dlen, colors,clen,vmax,cmax);
		duAppendCylinderWire(bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], duDarkenCol(col),data, dlen, colors, clen, vmax, cmax);
	}
}

RECAST_API void get_obstacle_graphics(Race* ptr, FPRaw* data, int& dlen, unsigned int* colors, int& clen)
{
	int length = dlen;
	dlen = 0;
	int clength = clen;
	clen = 0;
	if (!ptr || !data || !colors)
	{
		return;
	}

	const dtTileCache* tileCache = ptr->get_tilecache();
	if (!tileCache)
	{
		return;
	}

	drawObstacles(tileCache, data, dlen,colors, clen, length, clength);
}